﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Vk.Authorization;

namespace Vk
{
    /// <summary>
    ///     Query for a VkAPI request
    /// </summary>
    public struct ApiQuery
    {
        private readonly string _method;
        private readonly Dictionary<string, string> _paramData;

        /// <summary>
        ///     Initializes a new query
        /// </summary>
        /// <param name="method">Name of the VkAPI method</param>
        /// <param name="noAuth">true if this method can be performed without authorization</param>
        public ApiQuery(string method, bool noAuth = false)
        {
            _method = method;
            CanNoAuth = noAuth;
            _paramData = new Dictionary<string, string>();
        }

        /// <summary>
        ///     Whether this query needs an access token
        /// </summary>
        internal bool CanNoAuth { get; }

        #region Add overloads

        public ApiQuery Add(string key, string value)
        {
            if (value != null)
            {
                _paramData.Add(key, value);
            }
            return this;
        }

        public ApiQuery Add(string key, bool? value)
        {
            if (value != null)
            {
                _paramData.Add(key, value.Value ? "1" : "0");
            }
            return this;
        }

        public ApiQuery Add(string key, int? value)
        {
            if (value != null)
            {
                _paramData.Add(key, value.ToString());
            }
            return this;
        }

        public ApiQuery Add(string key, double? value)
        {
            if (value != null)
            {
                _paramData.Add(key, value.ToString());
            }
            return this;
        }

        public ApiQuery Add<T>(string key, IEnumerable<T> values)
        {
            if (values != null && values.Any())
            {
                _paramData.Add(key, string.Join(",", values));
            }
            return this;
        }

        public ApiQuery Add(string key, DateTime? value)
        {
            if (value != null)
            {
                _paramData.Add(key, (value.Value.Second - new DateTime(1970, 1, 1).Second).ToString("D"));
            }
            return this;
        }

        #endregion

        /// <summary>
        ///     Removes all the parameters
        /// </summary>
        public void Clear()
        {
            _paramData.Clear();
        }

        public string ToString(VkSession session)
        {
            var sb = new StringBuilder("https://api.vk.com/method/");
            sb.Append(_method);
            sb.Append("?");

            _paramData.Add("api_id", session.AppId.ToString(CultureInfo.InvariantCulture));
            if (session.IsValid())
            {
                _paramData.Add("access_token", session.AccessToken);
            }
            else if (!CanNoAuth)
            {
                throw new ArgumentException("Session isn't valid: " + session, nameof(session));
            }

            _paramData.Add("v", Api.Version);

            // Сортировка параметров
            var sortedParams = new List<KeyValuePair<string, string>>(_paramData);
            sortedParams.Sort(
                (keyfirst, keylast) => string.Compare(keyfirst.Key, keylast.Key, StringComparison.Ordinal));

            sb.Append(
                sortedParams.Select(x => x.Key.ToString() + "=" + x.Value.ToString()).Aggregate((a, b) => a + "&" + b));
            return sb.ToString();
        }
    }
}